// ==UserScript==
// @name         生财有术志愿者辅助插件-峰行
// @namespace    http://tampermonkey.net/
// @version      2025-03-13
// @description  try to take over the world!
// @author       峰行 18366335922
// @match        https://*scys.com/activity/task/volunteer*
// @icon         https://scys.com/favicon.ico
// @grant        none
// ==/UserScript==

(function () {
    'use strict';

    const FX = window.FX = {
        addStyle(styleText) {
            var styleElement = document.createElement('style');
            styleElement.setAttribute('type', 'text/css')
            styleElement.innerHTML = styleText;
            document.head.appendChild(styleElement);
        },
        copyText(text) {
            navigator.clipboard.writeText(text).then(res => FX.showToast('已复制：\n' + text)).catch(err => FX.showToast('复制失败：\n' + err))
        },
        showToast(message, duration = 1500) {
            document.body.insertAdjacentHTML('beforeend', `
                        <div style="
                            position: fixed;
                            top: 50%;
                            left: 50%;
                            transform: translateX(-50%);
                            padding: 10px 20px;
                            background: rgba(0,0,0,0.8);
                            color: #fff;
                            border-radius: 4px;
                            z-index: 9999;
                        ">${message}</div>
                    `);
            const toastElement = document.body.lastElementChild
            setTimeout(() => toastElement.remove(), duration);
        },
        createButton(name, onClick, onCreate) {
            const button = document.createElement('button');
            button.innerText = name;
            onClick && button.addEventListener('click', onClick);
            onCreate && onCreate(button)
            return button;
        },
        createHoverMenu({ element, onMouseEnter, onMouseLeave, buttons }) {
            var hoverbtnsElement
            element.addEventListener('mouseenter', () => {
                if (element.querySelector('.md-hoverbtns')) { return }

                hoverbtnsElement = document.createElement('div');
                hoverbtnsElement.className = 'md-hoverbtns';
                const data = onMouseEnter && onMouseEnter(element)
                buttons.forEach(({ name, onClick, onCreate }) => {
                    hoverbtnsElement.appendChild(FX.createButton(name, e => {
                        hoverbtnsElement.remove()
                        onClick && onClick(e, data)
                    }, btn => {
                        onCreate && onCreate(btn, data)
                    }
                    ));
                });
                element.appendChild(hoverbtnsElement);
            });
            element.addEventListener('mouseleave', () => {
                hoverbtnsElement && hoverbtnsElement.remove();
                onMouseLeave && onMouseLeave()
            });
        },
        createHoverEditor({ element, value, onSave }) {

            const editDiv = document.createElement('div');
            editDiv.id = 'edit';
            editDiv.className = 'md-hoverbtns';
            editDiv.style.top = '0';
            editDiv.style.left = '0';
            editDiv.style.width = element.clientWidth + 'px';

            const overlayerDiv = document.createElement('div');
            overlayerDiv.className = 'overlayer';
            overlayerDiv.addEventListener('click', () => {
                editDiv.remove();
            });

            const inputElement = document.createElement('input');
            inputElement.style.width = element.clientWidth + 'px';
            inputElement.style.height = element.clientHeight + 'px';
            inputElement.value = value;
            inputElement.focus();
            inputElement.addEventListener('click', e => e.stopPropagation());

            const saveBtn = document.createElement('button');
            saveBtn.textContent = '保存';
            saveBtn.addEventListener('click', () => {
                if (inputElement.value === value) {
                    editDiv.remove()
                } else {
                    onSave(inputElement.value, value) && editDiv.remove()
                }
            });

            editDiv.appendChild(overlayerDiv);
            editDiv.appendChild(inputElement);
            editDiv.appendChild(saveBtn);
            element.appendChild(editDiv);
        },
        createCheckboxPopup({ onCreatePopup, options, onMoveOption, onGetIndex }) {
            const popup = document.createElement('div');
            popup.id = 'md-popup';
            popup.style.width = '200px'
            popup.style.maxHeight = '700px'
            popup.style.overflowY = 'scroll'
            onCreatePopup && onCreatePopup(popup)

            popup.insertOption = popup.createOption = ({ key, onCreateOption, checked, onCheckChange }, index) => {
                const label = document.createElement('label');
                label.key = label.htmlFor = key

                const checkbox = document.createElement('input');
                checkbox.type = 'checkbox';
                checkbox.value = checkbox.id = key;
                checkbox.checked = checked;
                checkbox.addEventListener('change', () => {
                    onCheckChange && onCheckChange(checkbox.checked)
                })

                const upButton = document.createElement('button');
                upButton.innerHTML = ' ↑ ';
                upButton.addEventListener('click', () => moveOption(key, -1));

                const downButton = document.createElement('button');
                downButton.innerHTML = ' ↓ ';
                downButton.addEventListener('click', () => moveOption(key, 1));

                label.appendChild(upButton);
                label.appendChild(downButton);
                label.appendChild(checkbox);
                if (onCreateOption) {
                    label.appendChild(onCreateOption(index, label, popup))
                } else {
                    const span = document.createElement('span');
                    span.innerHTML = checkbox.value
                    label.appendChild(span)
                }
                popup.insertBefore(label, popup.children[index + 1]);
            }

            options.forEach(popup.createOption);

            function moveOption(key, direction) {
                const index = onGetIndex(key)
                const directionIndex = index + direction
                if (index !== -1 && directionIndex >= 0 && directionIndex < options.length) {
                    if (direction > 0) {
                        popup.insertBefore(popup.children[directionIndex], popup.children[index])
                    } else {
                        popup.insertBefore(popup.children[index], popup.children[directionIndex])
                    }
                    onMoveOption(index, directionIndex)
                }
            }

            const overlay = document.createElement('div');
            overlay.id = 'md-overlay';
            overlay.addEventListener('click', function () {
                document.body.removeChild(popup);
                document.body.removeChild(overlay);
            });

            document.body.appendChild(overlay);
            document.body.appendChild(popup);
        },
        saveValueToLocal(key, value) {
            localStorage.setItem(key, JSON.stringify(value));
        },
        getValueFromLocal(key, defVal) {
            var savedValue = localStorage.getItem(key);
            try {
                savedValue = JSON.parse(savedValue)
            } catch (e) { }
            return savedValue === null ? defVal : savedValue;
        },
        clearLocalValue(key) {
            if (key) {
                localStorage.removeItem(key)
            } else {
                localStorage.clear()
            }
        },
        waitForElement(selector, delay = 0) {
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    const targetNode = document.querySelector(selector);
                    if (targetNode) {
                        resolve(targetNode);
                    } else {
                        const observer = new MutationObserver((mutations) => {
                            mutations.forEach((mutation) => {
                                const targetNode = document.querySelector(selector);
                                if (targetNode) {
                                    observer.disconnect();
                                    resolve(targetNode);
                                }
                            });
                        });
                        observer.observe(document.documentElement, { childList: true, subtree: true });
                    }
                }, delay);
            });
        },
        downloadZipFiles(zipFilename, files) {
            FX.showToast(`正在生成文件：${zipFilename}`)
            const tasks = files.map(({ filename, url }) => {
                return new Promise((resolve, reject) => {
                    const xhr = new XMLHttpRequest();
                    xhr.open("GET", url, true);
                    xhr.responseType = "blob";
                    xhr.onload = () => xhr.status === 200 ? resolve({ filename, blob: xhr.response }) : reject(new Error("Failed to download image: " + url))
                    xhr.onerror = () => reject(new Error("Failed to download image: " + url));
                    xhr.send();
                });
            })
            return Promise.all(tasks)
                .then(blobs => {
                    const zip = new JSZip();
                    blobs.forEach(({ filename, blob }) => {
                        zip.file(filename, blob, { binary: true })
                    })
                    return zip.generateAsync({ type: "blob" });
                })
                .then(zipBlob => {
                    FX.showToast(`准备下载文件：${zipFilename}`);
                    saveAs(zipBlob, zipFilename)
                })
                .catch(error => console.error("Failed to create ZIP file:", error));
        }
    }

    function requestPost(url, body) {
        return new Promise((resolve, reject) => {
            fetch(url, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                    'Authorization': 'Bearer your_token_here'
                },
                body: JSON.stringify(body)
            })
            .then(response => {
                if (!response.ok) {
                    reject(response.status);
                }
                return response.json();
            })
            .then(data => {
                resolve(data);
            })
            .catch(error => {
                reject(error.message);
            });
        })
    }

    // 添加浮动按钮-获取成员资料
    function addUserListCopyBtn(css) {
        let btn = document.createElement("button");
        btn.innerHTML = "获取成员资料";
        btn.style = "position: fixed; top: 120px; right: 8px; z-index: 9999; width: 120px; height: 50px; background-color: #4CAF50; color: white; font-size: 16px;";
        btn.onclick = function () {
            // 航海成员的信息卡片
            let users = Array.from(document.querySelectorAll(".cards-volunteer"))
                .map((e, index) => {
                    // 依次提取：序号，微信昵称，微信号，手机号，群昵称，上传进度，备注
                    let oneUser = [index + 1];
                    Array.from(e.querySelectorAll(".middle .common")).map(e => {
                        oneUser.push(e.children[1].innerText);
                        if (e.children[0].innerText == "群昵称：") {
                            // 将群昵称按照“-”分割，只取第一个部分，避免某些船员的微信昵称是空格
                            let customNickname = e.children[1].innerText.split("-").filter(e => e)[0];
                            oneUser.push(customNickname + " AI工具站 生财");
                        }

                    })
                    return oneUser.join("\t");
                });

            // 数据添加一个表头
            let header = "序号\t微信昵称\t微信号\t手机号\t群昵称\t备注昵称\t上传进度\t备注";
            users.unshift(header);

            // 复制到剪贴板
            navigator.clipboard.writeText(users.join("\n"));

            // 弹窗提醒，“已经复制成功，请前往excel中粘贴”
            alert("已经复制成功，请前往excel中粘贴");
        };
        document.body.appendChild(btn);
    }

    // 添加浮动按钮-获取今日打卡情况
    function addUnlogUserListBtn() {
        let btn = document.createElement("button");
        btn.innerHTML = "今日打卡汇总";
        btn.style = "position: fixed; top: 170px; right: 8px; z-index: 9999; width: 120px; height: 50px; background-color:rgb(231, 172, 43); color: white; font-size: 16px;";
        btn.onclick = function () {
            // 航海成员的信息卡片
            let users = Array.from(document.querySelectorAll(".cards-volunteer"))
                .map((e, index) => {
                    // 依次提取：序号，微信昵称，打开状态
                    let oneUser = [index + 1];
                    let nickname = e.querySelector(".groupName > span").innerText;
                    oneUser.push(nickname);
                    let isLogText = e.querySelector(".right > span").innerText;
                    if (isLogText == "今日未打卡") {
                        oneUser.push("未打卡");
                    } else {
                        oneUser.push("done");
                    }
                    return oneUser.join("\t");
                });

            // 数据添加一个表头
            let header = "序号\t微信昵称\t打卡记录";
            users.unshift(header);

            // 复制到剪贴板
            navigator.clipboard.writeText(users.join("\n"));

            // 弹窗提醒，“已经复制成功，请前往excel中粘贴”
            alert("已经复制成功，请前往excel中粘贴");
        };
        document.body.appendChild(btn);
    }

    // 添加浮动按钮-获取今日未打卡成员列表，用于微信发送提醒消息
    function addUnlogUserListForWxPushBtn() {
        let btn = document.createElement("button");
        btn.innerHTML = "未打卡成员";
        btn.style = "position: fixed; top: 220px; right: 8px; z-index: 9999; width: 120px; height: 50px; background-color:rgb(25, 109, 235); color: white; font-size: 16px;";
        btn.onclick = function () {
            // 航海成员的信息卡片
            let wxUserList = [];
            Array.from(document.querySelectorAll(".cards-volunteer"))
                .map((e) => {
                    // 依次提取：序号，微信昵称，打卡状态
                    let nickname = e.querySelector(".groupName > span").innerText;
                    let isLogText = e.querySelector(".right > span").innerText;
                    if (isLogText == "今日未打卡") {
                        // 通过正则表达式，将nickname中的中文字符和英文字符提取出来
                        nickname = nickname.replace(/[^\u4e00-\u9fa5a-zA-Z0-9]/g, "");
                        wxUserList.push(`${nickname} AI工具站 生财`);
                    }
                });

            // 复制到剪贴板
            navigator.clipboard.writeText(JSON.stringify(wxUserList));

            // 弹窗提醒，“已经复制成功，请前往excel中粘贴”
            alert("已经复制成功，请前往python脚本中粘贴");
        };
        document.body.appendChild(btn);
    }

    // 添加浮动按钮-获取所有成员列表，用于微信发送提醒消息
    function addAllUserListForWxPushBtn() {
        let btn = document.createElement("button");
        btn.innerHTML = "所有成员";
        btn.style = "position: fixed; top: 270px; right: 8px; z-index: 9999; width: 120px; height: 50px; background-color:rgb(85, 189, 135); color: white; font-size: 16px;";
        btn.onclick = function () {
            // 航海成员的信息卡片
            let wxUserList = [];
            Array.from(document.querySelectorAll(".cards-volunteer"))
                .map((e) => {
                    // 依次提取：序号，微信昵称，打开状态
                    let nickname = e.querySelector(".groupName > span").innerText;
                    let isLogText = e.querySelector(".right > span").innerText;
                    // 通过正则表达式，将nickname中的中文字符和英文字符提取出来
                    nickname = nickname.replace(/[^\u4e00-\u9fa5a-zA-Z0-9]/g, "");
                    wxUserList.push(`${nickname} AI工具站 生财`);
                });

            // 复制到剪贴板
            navigator.clipboard.writeText(JSON.stringify(wxUserList));

            // 弹窗提醒，“已经复制成功，请前往excel中粘贴”
            alert("已经复制成功，请前往python脚本中粘贴");
        };
        document.body.appendChild(btn);
    }


    // 显示浮动窗口
    function showMembersTable(data) {
        // 创建样式元素
        const style = document.createElement('style');
        style.textContent = `
            .members-modal {
                position: fixed;
                top: 0;
                left: 0;
                right: 0;
                bottom: 0;
                background-color: rgba(0,0,0,0.5);
                display: flex;
                justify-content: center;
                align-items: center;
                z-index: 1000;
            }
            .members-container {
                background-color: white;
                border-radius: 8px;
                box-shadow: 0 4px 20px rgba(0,0,0,0.15);
                width: 80%;
                max-width: 1200px;
                max-height: 80vh;
                overflow: auto;
                padding: 20px;
                animation: fadeIn 0.3s ease-out;
            }
            @keyframes fadeIn {
                from { opacity: 0; transform: translateY(-20px); }
                to { opacity: 1; transform: translateY(0); }
            }
            .members-header {
                display: flex;
                justify-content: space-between;
                align-items: center;
                margin-bottom: 15px;
                padding-bottom: 10px;
                border-bottom: 1px solid #eee;
            }
            .members-title {
                font-size: 17px;
                font-weight: bold;
                color: #333;
            }
            .members-close {
                background: none;
                border: none;
                font-size: 17px;
                cursor: pointer;
                color: #999;
            }
            .members-table {
                width: 100%;
                border-collapse: collapse;
                margin-top: 10px;
            }
            .members-table th, .members-table td {
                padding: 12px 15px;
                text-align: left;
                border-bottom: 1px solid #e0e0e0;
            }
            .members-table th {
                background-color: #f8f9fa;
                font-weight: 600;
                color: #495057;
            }
            .members-table tr:hover {
                background-color: #f8f9fa;
            }
            .remind-btn {
                background-color: #4e73df;
                color: white;
                border: none;
                padding: 6px 12px;
                border-radius: 4px;
                cursor: pointer;
                transition: background-color 0.2s;
            }
            .remind-btn:hover {
                background-color: #3a56c4;
            }
            .copy-textarea {
                width: 100%;
                min-height: 60px;
                padding: 8px;
                border: 1px solid #ddd;
                border-radius: 4px;
                resize: vertical;
                font-family: inherit;
            }
        `;
        document.head.appendChild(style);

        // 创建模态框容器
        const modal = document.createElement('div');
        modal.className = 'members-modal';

        const container = document.createElement('div');
        container.className = 'members-container';

        // 创建头部
        const header = document.createElement('div');
        header.className = 'members-header';

        const title = document.createElement('div');
        title.className = 'members-title';
        title.textContent = '成员列表';

        const closeBtn = document.createElement('button');
        closeBtn.className = 'members-close';
        closeBtn.innerHTML = '&times;';
        closeBtn.onclick = () => modal.remove();

        header.appendChild(title);
        header.appendChild(closeBtn);

        // 创建表格
        const table = document.createElement('table');
        table.className = 'members-table';

        // 表头
        const thead = document.createElement('thead');
        const headerRow = document.createElement('tr');
        ['序号', '昵称', '微信号', '打卡统计', '打卡状态', '是否围观', '文案', '操作'].forEach(text => {
            const th = document.createElement('th');
            th.textContent = text;
            headerRow.appendChild(th);
        });
        thead.appendChild(headerRow);
        table.appendChild(thead);

        // 表体
        const tbody = document.createElement('tbody');
        data.forEach(async (item) => {
            const row = document.createElement('tr');

            // 序号
            const indexCell = document.createElement('td');
            indexCell.textContent = item.index || '';

            // 昵称
            const nameCell = document.createElement('td');
            nameCell.textContent = item.nickname || '';

            // 微信号
            const wxidCell = document.createElement('td');
            wxidCell.textContent = item.wxid || '';

            // 打卡统计
            const logAnyCell = document.createElement('td');
            logAnyCell.textContent = `${item.logTotal || ''}卡/${item.rest || ''}休`;

            // 状态
            const statusCell = document.createElement('td');
            statusCell.textContent = item.logText || '';

            // 围观
            const weiguanCell = document.createElement('td');
            weiguanCell.textContent = item.weiguan;

            // 文案输入框
            const copyCell = document.createElement('td');
            const textarea = document.createElement('textarea');
            textarea.className = 'copy-textarea';
            textarea.rows = 3;
            textarea.placeholder = '请输入提醒文案...';
            copyCell.appendChild(textarea);

            // 操作按钮
            const actionCell = document.createElement('td');
            const button = document.createElement('button');
            button.className = 'remind-btn';
            button.textContent = '发送消息';
            button.onclick = async () => {
                const copyText = textarea.value;
                const res = await requestPost('http://localhost:3000/api/remindOne', {
                    nickname: '',
                    wxid: item.wxid,
                    text: copyText || '你好'
                })
                if (res.errno == 0) {
                    alert(`成功提醒${item.nickname} 打卡\n文案内容：${copyText || '无文案'}`);
                } else {
                    alert(`服务异常: ${res.errmsg}`);
                }
            };
            actionCell.appendChild(button);

            // 组装行
            row.appendChild(indexCell);
            row.appendChild(nameCell);
            row.appendChild(wxidCell);
            row.appendChild(logAnyCell);
            row.appendChild(statusCell);
            row.appendChild(weiguanCell);
            row.appendChild(copyCell);
            row.appendChild(actionCell);

            tbody.appendChild(row);
        });

        table.appendChild(tbody);

        // 组装容器
        container.appendChild(header);
        container.appendChild(table);
        modal.appendChild(container);

        // 添加到文档
        document.body.appendChild(modal);
    }

    // 添加浮动按钮-调用接口发送微信消息
    function addRemindOneBtn() {
        let btn = document.createElement("button");
        btn.innerHTML = "提醒圈友";
        btn.style = "position: fixed; top: 320px; right: 8px; z-index: 9999; width: 120px; height: 50px; background-color:rgba(151, 58, 29, 1); color: white; font-size: 16px;";
        btn.onclick = async function () {
            // 航海成员的信息卡片
            let users = Array.from(document.querySelectorAll(".cards-volunteer"))
                .map((e, i) => {
                    // 依次提取：序号，微信昵称，微信号，手机号，群昵称，上传进度，备注
                    const [nickname, wxid, mobile, scNickname] = Array.from(e.querySelectorAll(".middle .common")).map(e => e.children[1].innerText);
                    // 取打卡状态
                    // let nickname = e.querySelector(".groupName > span").innerText;
                    const logText = e.querySelector(".right > span").innerText;
                    // 获取围观标记
                    let weiguan = '';
                    const weiguanEle = e.querySelector(".groupName .onlooker span");
                    if (weiguanEle) weiguan = weiguanEle.innerText;
                    // 打卡次数
                    const logTotalEle = e.querySelector(".bottom.v-row span:first-child");
                    const logTotal = logTotalEle.innerText;
                    // 休息天数
                    const restEle = e.querySelector(".bottom.v-row span:last-child");
                    const rest = restEle.innerText;

                    return {index: i+1, nickname, wxid, mobile, scNickname, logText, weiguan, logTotal, rest};
                });


            showMembersTable(users);
            // const res = await requestPost('http://localhost:3000/api/remindOne', {
            //     nickname: '',
            //     wxid: 'wxid_3842528425415',
            //     text: '打卡'
            // })
            // // 航海成员的信息卡片
            // let wxUserList = [];
            // Array.from(document.querySelectorAll(".cards-volunteer"))
            //     .map((e) => {
            //         // 依次提取：序号，微信昵称，打开状态
            //         let nickname = e.querySelector(".groupName > span").innerText;
            //         let isLogText = e.querySelector(".right > span").innerText;
            //         // 通过正则表达式，将nickname中的中文字符和英文字符提取出来
            //         nickname = nickname.replace(/[^\u4e00-\u9fa5a-zA-Z0-9]/g, "");
            //         wxUserList.push(`${nickname} AI工具站 生财`);
            //     });

            // // 复制到剪贴板
            // navigator.clipboard.writeText(JSON.stringify(wxUserList));

            // // 弹窗提醒，“已经复制成功，请前往excel中粘贴”
            // alert("已经复制成功，请前往python脚本中粘贴");
        };
        document.body.appendChild(btn);
    }

    /*
    document.onreadystatechange = function () {
        if (document.readyState == "complete") {
            // 添加浮动按钮-获取成员资料
            addUserListCopyBtn();

            // 添加浮动按钮-获取今日未打卡成员
            addUnlogUserListBtn();

            // 添加浮动按钮-获取今日未打卡成员列表，用于微信发送提醒消息
            addUnlogUserListForWxPushBtn();

            // 添加浮动按钮-获取所有成员列表，用于微信发送提醒消息
            addAllUserListForWxPushBtn();

            // 添加浮动按钮-调用接口发送微信消息
            addRemindOneBtn();
        }
    };*/

    FX.waitForElement('.cards-volunteer').then(firstCardElement => {
        // 添加浮动按钮-获取成员资料
            addUserListCopyBtn();

            // 添加浮动按钮-获取今日未打卡成员
            addUnlogUserListBtn();

            // 添加浮动按钮-获取今日未打卡成员列表，用于微信发送提醒消息
            addUnlogUserListForWxPushBtn();

            // 添加浮动按钮-获取所有成员列表，用于微信发送提醒消息
            addAllUserListForWxPushBtn();

            // 添加浮动按钮-调用接口发送微信消息
            addRemindOneBtn();
    })
})();